// src/api/GraphqlConfig.tsx
// 创建一个 HTTP link
import {ApolloClient, ApolloLink, HttpLink, InMemoryCache, Observable} from "@apollo/client";
import {STORAGE_KEY} from "../config/Constant.tsx";
import {authService} from "./service/AuthService.tsx";


const apiUrl = import.meta.env.VITE_API_BASE_URL;
const httpLink = new HttpLink({ uri: apiUrl + '/api/graphql' });

// 401 错误处理 Link
const authLink = new ApolloLink((operation, forward) => {
    // 应用启动时尝试从 localStorage 获取 user_info
    const storedUserInfo = localStorage.getItem(STORAGE_KEY.USER_INFO);
    if (storedUserInfo) {
        const parsedUserInfo = JSON.parse(storedUserInfo);
        if(parsedUserInfo && parsedUserInfo.access_token) {
            operation.setContext({
                headers: {
                    Authorization: `Bearer ${parsedUserInfo.access_token}`,
                },
            });
        }
    }

    return new Observable(observer => {
        let isRetry = false;

        const attemptRequest = () => {
            const subscription = forward(operation).subscribe({
                next: async (result) => {
                    // 检查是否有UNAUTHORIZED错误且尚未重试
                    const hasUnauthorizedError = result.errors?.some(
                        error => error.extensions?.code === 'UNAUTHORIZED'
                    );

                    if (hasUnauthorizedError && !isRetry) {
                        try {
                            // 标记为重试状态
                            isRetry = true;

                            // 尝试刷新token
                            const tokens = await authService.refreshToken();

                            if (tokens?.access_token) {
                                // 更新localStorage中的token
                                const user = localStorage.getItem(STORAGE_KEY.USER_INFO);
                                if (user) {
                                    const userInfo = JSON.parse(user);
                                    userInfo.access_token = tokens.access_token;
                                    localStorage.setItem(
                                        STORAGE_KEY.USER_INFO,
                                        JSON.stringify(userInfo)
                                    );
                                }

                                // 更新请求头中的token
                                operation.setContext({
                                    headers: {
                                        Authorization: `Bearer ${tokens.access_token}`,
                                    },
                                });

                                // 取消当前订阅
                                subscription.unsubscribe();

                                // 重新尝试请求
                                attemptRequest();
                            } else {
                                // 刷新失败，清除用户信息并返回原始结果
                                localStorage.removeItem(STORAGE_KEY.USER_INFO);
                                observer.next(result);
                                observer.complete();
                            }
                        } catch (error) {
                            // 刷新token失败，清除用户信息并返回原始结果
                            localStorage.removeItem(STORAGE_KEY.USER_INFO);
                            console.error("Token refresh failed:", error);
                            observer.next(result);
                            observer.complete();
                        }
                    } else {
                        // 没有错误或已经重试过，直接返回结果
                        observer.next(result);
                        observer.complete();
                    }
                },
                error: (err) => observer.error(err),
                complete: () => observer.complete(),
            });

            return subscription;
        };

        const subscription = attemptRequest();

        return () => subscription.unsubscribe();
    });
});

// 组合 links
const link = authLink.concat(httpLink);

const graphqlClient = new ApolloClient({
    link,
    cache: new InMemoryCache(),
});

export default graphqlClient;
